#ifndef CAPABILITYMETHOD_H
#define CAPABILITYMETHOD_H

#include <memory>
#include <functional>
#include <memory>
#include <future>
#include <stdexcept>
#include <utility>

#include <QJsonObject>

// forward declear
template <typename F>
class TaskImpl;

/**
 * @brief Base class for tasks that can be executed asynchronously.
 */
class Task
{
public:
    virtual ~Task() = default;

    /**
     * @brief Execute the task with parameters
     * @param params - JSON object containing execution parameters
     * @return QJsonObject - execution result
     */
    virtual QJsonObject execute(const QJsonObject& params) = 0;

    /**
     * @brief Get the type name of the task for debugging or type checking.
     * @return const char* - type name
     */
    virtual const char* typeName() const = 0;
};

/**
 * @brief Task implementation that wraps a function object.
 * @tparam F - type of the function object
 */
template <typename F>
class TaskImpl : public Task
{
public:
    explicit TaskImpl(F&& func)
        : mTask(std::forward<F>(func)) {}

    QJsonObject execute(const QJsonObject& params) override {
        mTask(params);
        return mTask.get_future().get();
    }

    std::future<QJsonObject> getFuture() {
        return mTask.get_future();
    }

    const char* typeName() const override {
        return typeid(F).name();
    }

private:
    std::packaged_task<QJsonObject(const QJsonObject&)> mTask;
};

/**
 * @brief Create a task from a function object. It is a support method.
 * @tparam F - type of the function object
 * @param func - function object to be wrapped in a task
 * @return std::shared_ptr<Task> - shared pointer to the created task
 */
template <typename F>
std::shared_ptr<Task> createTask(F&& func)
{
    return std::shared_ptr<Task>(
        new TaskImpl<F>(std::forward<F>(func))
    );
}

/**
 * @brief Package a member function as a task.
 * @tparam Class - type of the class that contains the member function
 * @tparam Ret - return type of the member function
 * @param obj - pointer to the object instance
 * @param func - pointer to the member function
 * @return std::unique_ptr<Task> - unique pointer to the created task
 */
template <typename Class, typename Ret>
std::shared_ptr<Task> packageAsTask(Class* obj, Ret (Class::*func)(const QJsonObject&))
{
    auto bound_func = [obj, func](const QJsonObject& params) -> QJsonObject {
        return (obj->*func)(params);
    };
    return createTask(std::move(bound_func));
}

/**
 * @brief Package a **const** member function as a task.
 */
template <typename Class, typename Ret>
std::shared_ptr<Task> packageAsTask(const Class* obj, Ret (Class::*func)(const QJsonObject&) const)
{
    auto bound_func = [obj, func](const QJsonObject& params) -> QJsonObject {
        return (obj->*func)(params);
    };
    return createTask(std::move(bound_func));
}

/**
 * @brief CapabilityMethod structure that combines method description and task
 * 
 * This structure contains both the method description (JSON schema) and 
 * the associated task that can be executed when the method is called.
 */
struct CapabilityMethod
{
    /**
     * @brief JSON description of the method (name, title, description, inputSchema, outputSchema)
     */
    QJsonObject describe;
    
    /**
     * @brief Task object that will be executed when the method is called
     * 
     * The task should be callable with a QJsonObject parameter and return QJsonObject result.
     */
    std::shared_ptr<Task> task;
    
    /**
     * @brief Default constructor
     */
    CapabilityMethod() = default;
    
    /**
     * @brief Constructor with description and task
     * @param desc - Method description JSON
     * @param t - Task object (will be moved)
     */
    CapabilityMethod(QJsonObject desc, std::shared_ptr<Task> t = nullptr)
        : describe(std::move(desc)), task(std::move(t)) {}
    
    /**
     * @brief Move constructor
     */
    CapabilityMethod(CapabilityMethod&& other) noexcept
        : describe(std::move(other.describe)), task(std::move(other.task)) {}
    
    /**
     * @brief Move assignment operator
     */
    CapabilityMethod& operator=(CapabilityMethod&& other) noexcept {
        if (this != &other) {
            describe = std::move(other.describe);
            task = std::move(other.task);
        }
        return *this;
    }
    
    // Disable copy operations
    CapabilityMethod(const CapabilityMethod&) = delete;
    CapabilityMethod& operator=(const CapabilityMethod&) = delete;
};

#endif // CAPABILITYMETHOD_H
